package watercalc;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

public class Graph {

	public static final int HEIGHTS = 0;
	public static final int CAPACITIES = 1;
	
	public int mRows;
	public int mColumns;
	public ArrayList<Vertex> mVertexes;
	
	Graph(int iRows, int iColumns, ArrayList<Vertex> iVertexes) {
		mRows = iRows;
		mColumns = iColumns;
		mVertexes = iVertexes;
		mVertexes.add(new Vertex()); // This vertex represents the outside of the container, we will refer to it as the "sink"
	}
	
	/**
	 * Displays graph internal status echoing to std::cout.
	 * \param DisplayMode iDisplayMode which vertex attribute to display
	 */
	public void display(int iDisplayMode) {
		for (int r=0; r<mRows; ++r) {
			for (int c=0; c<mColumns; ++c) {
				switch (iDisplayMode) {
				case CAPACITIES:
					if ( !mVertexes.get(c+r*mColumns).stable )
						System.out.print("? ");
					else
						System.out.print(mVertexes.get(c+r*mColumns).capacity + " ");
					break;
				case HEIGHTS:
					System.out.print(mVertexes.get(c+r*mColumns).height + " ");
					break;
				}
			}
			System.out.println("");
		}
	}
	
	/**
	 * Builds list of pointer to neighbour vertexes on each vertex based on the implicit geometry
	 * of the container.
	 * \see Readme.txt for explanations on adjacency mode
	 * \param AdjacencyMode iAdjacencyMode
	 */
	public void buildAdjacencyLists(String iAdjacencyMode) {
		LinkedList<Integer> neighboursIndexes;
		Iterator<Integer> iterator; 
		for (int c=0; c<=mVertexes.size(); ++c) {
			neighboursIndexes = getNeighboursIndexes(c, iAdjacencyMode);
			iterator = neighboursIndexes.iterator();
			while (iterator.hasNext()){
				mVertexes.get(c).neighbours.add(mVertexes.get(iterator.next())); 
			}
		}
	}
	
	private LinkedList<Integer> getNeighboursIndexes(int iIndex, String iAdjacencyMode) {
		LinkedList<Integer> neighbours = new LinkedList<Integer>();

		if (iIndex==mRows * mColumns) {
			
			// We are querying for sink's neighbours
			for (int r=0; r<mRows*mColumns; ++r) {
				if (r<mColumns || r>mColumns*(mRows-1) || r%mColumns == 0 || r%mColumns == mColumns-1) {
					neighbours.add(r);
				}
			}

		} else if (iIndex<mRows * mColumns) {

			// We are querying for another cell's neighbours
			double tmp = iIndex / mColumns;
			int r = (int) Math.ceil((double) tmp);
			int c = iIndex % mColumns;

			if (r>0)			neighbours.add(c + (r - 1) * mColumns);
			if (r<mRows - 1)	neighbours.add(c + (r + 1) * mColumns);
			if (c>0)			neighbours.add(c - 1 + r * mColumns);
			if (c<mColumns - 1)	neighbours.add(c + 1 + r * mColumns);

			if ( iAdjacencyMode == Arguments.ADJACENCY8 ) {
				if (c>0 && r>0)						neighbours.add(c - 1 + (r - 1) * mColumns);
				if (c<mColumns - 1 && r<mRows - 1)	neighbours.add(c + 1 + (r + 1) * mColumns);
				if (c>0 && r<mRows - 1)				neighbours.add(c - 1 + (r + 1) * mColumns);
				if (c<mColumns - 1 && r>0 )			neighbours.add(c + 1 + (r - 1) * mColumns);
			}
				
			if (r==0 || r==mRows - 1 || c==0 || c== mColumns - 1) neighbours.add(mColumns * mRows);
		}

		return neighbours;
	}
	
}